
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "storage.h"
#include "utils.h"

extern int doLogging; //Logging variable

void* storage_connect(const char *hostname, const int port) {
    char buffer[50];

    // Create a socket.
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock < 0)
        return NULL;

    // Get info about the server.
    struct addrinfo serveraddr, *res;
    memset(&serveraddr, 0, sizeof serveraddr);
    serveraddr.ai_family = AF_UNSPEC;
    serveraddr.ai_socktype = SOCK_STREAM;
    char portstr[MAX_PORT_LEN];
    snprintf(portstr, sizeof portstr, "%d", port);
    int status = getaddrinfo(hostname, portstr, &serveraddr, &res);
    if (status != 0){
        snprintf(buffer, sizeof buffer, "Connection with server unsuccessful\n");
        logger(clientFile, buffer, doLogging);
        //If connection with server unsuccessful -> give unsuccessful log
        return NULL;
    }

    // Connect to the server.
    status = connect(sock, res->ai_addr, res->ai_addrlen);
    if (status != 0){
        snprintf(buffer, sizeof buffer, "Connection with server unsuccessful\n");
        logger(clientFile, buffer, doLogging);
        //If connection with server unsuccessful -> give unsuccessful log
        return NULL;
    }

    snprintf(buffer, sizeof buffer, "Connection with server successful\n");
    logger(clientFile, buffer, doLogging);
    //If connection with server successful -> give successful log
    return (void*) sock;
}

int storage_auth(const char *username, const char *passwd, void *conn) {
    // Connection is really just a socket file descriptor.
    int sock = (int) conn;
    char buffer[50];

    // Send some data.
    char buf[MAX_CMD_LEN];
    memset(buf, 0, sizeof buf);
    char *encrypted_passwd = generate_encrypted_password(passwd, NULL);
    snprintf(buf, sizeof buf, "AUTH %s %s\n", username, encrypted_passwd);
    if (sendall(sock, buf, strlen(buf)) == 0 && recvline(sock, buf, sizeof buf) == 0) {

        snprintf(buffer, sizeof buffer, "Storage_auth with server successful\n");
        logger(clientFile, buffer, doLogging);
        //If authentication with server successful -> give successful log

        return 0;
    }

    snprintf(buffer, sizeof buffer, "Storage_auth with server unsuccessful\n");
    logger(clientFile, buffer, doLogging);
    //If authentication with server unsuccessful -> give unsuccessful log

    return -1;
}

int storage_get(const char *table, const char *key, struct storage_record *record, void *conn) {
    // Connection is really just a socket file descriptor.
    int sock = (int) conn;
    char buffer[50];

    // Send some data.
    char buf[MAX_CMD_LEN];
    memset(buf, 0, sizeof buf);
    snprintf(buf, sizeof buf, "GET %s %s\n", table, key);
    if (sendall(sock, buf, strlen(buf)) == 0 && recvline(sock, buf, sizeof buf) == 0) {

        snprintf(buffer, sizeof buffer, "Storage_get with server successful\n");
        logger(clientFile, buffer, doLogging);
        //If data retrieval to server successful -> give successful log

        return 0;
    }

    snprintf(buffer, sizeof buffer, "Storage_get with server unsuccessful\n");
    logger(clientFile, buffer, doLogging);
    //If data retrieval to server unsuccessful -> give unsuccessful log

    return -1;
}

int storage_set(const char *table, const char *key, struct storage_record *record, void *conn) {
    // Connection is really just a socket file descriptor.
    int sock = (int) conn;
    char buffer[50];

    // Send some data.
    char buf[MAX_CMD_LEN];
    memset(buf, 0, sizeof buf);
    snprintf(buf, sizeof buf, "SET %s %s %s\n", table, key, record->value);
    if (sendall(sock, buf, strlen(buf)) == 0 && recvline(sock, buf, sizeof buf) == 0) {

        snprintf(buffer, sizeof buffer, "Storage_set with server successful\n");
        logger(clientFile, buffer, doLogging);
        //If data modification with server successful -> give successful log

        return 0;
    }

    snprintf(buffer, sizeof buffer, "Storage_set with server unsuccessful\n");
    logger(clientFile, buffer, doLogging);
    //If data modification with server unsuccessful -> give unsuccessful log

    return -1;
}

int storage_disconnect(void *conn) {
    // Cleanup
    int sock = (int) conn;
    char buffer[50];


    snprintf(buffer, sizeof buffer, "Disconnected from server\n");
    logger(clientFile, buffer, doLogging);
    //If data transmission to server unsuccessful -> give unsuccessful log

    close(sock);

    return 0;
}

